package cn.seaboot.excel;

import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.xssf.model.SharedStrings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 工作簿处理函数
 * <p>
 * 用于对接 sax 解析
 * <p>
 * 已知问题：将 xls 文件另存为 xlsx 之后，可能会存在部分单元格无法解析，暂不确定原因。
 * 如果遇到这个问题，可以新建一个 xlsx 文件，将所有内容复制到新文件。
 *
 * @author Mr.css
 * @version 2025-05-27 14:38
 */
public class SheetHandler extends DefaultHandler {
    private final Logger logger = LoggerFactory.getLogger(SheetHandler.class);

    /**
     * 行级数据处理回调
     */
    private Consumer<List<String>> consumer;

    /**
     * 共享字符
     * <p>
     * 下拉框等特殊输入框
     */
    private SharedStrings sharedStrings;

    /**
     * 当前正在处理的行级数据
     */
    private List<String> row;
    /**
     * 当前单元格的 q-name
     */
    private String qName;
    /**
     * 当前单元格数据类型
     */
    private String cellType;


    public SharedStrings getSharedStrings() {
        return sharedStrings;
    }

    public void setSharedStrings(SharedStrings sharedStrings) {
        this.sharedStrings = sharedStrings;
    }

    public Consumer<List<String>> getConsumer() {
        return consumer;
    }

    public void setConsumer(Consumer<List<String>> consumer) {
        this.consumer = consumer;
    }

    /**
     * 打印所有的属性
     *
     * @param attributes dom-attributes
     */
    private void print(Attributes attributes) {
        logger.debug("current node attributes: {}", attributes);
        for (int i = 0; i < attributes.getLength(); i++) {
//            logger.debug("{}-{}", attributes.getQName(i), attributes.getValue(i));
            System.out.println(attributes.getQName(i) + "-" + attributes.getValue(i));
        }
    }

    /**
     * 接收元素开始的通知。
     * <p>
     * 每个元素的开始处采取特定的操作（例如分配新的树节点或将输出写入文件）。
     *
     * <pre>
     *      // 获取单元格的位置，如 A1,B1
     *      String row = attributes.getValue("r");
     *      // style id
     *      String style = attributes.getValue("s");
     *
     *      // data type：null or s or others
     *      String type = attributes.getValue("t");
     * </pre>
     *
     * @param uri        The Namespace URI, or the empty string if the
     *                   element has no Namespace URI or if Namespace
     *                   processing is not being performed.
     * @param localName  The local name (without prefix), or the
     *                   empty string if Namespace processing is not being
     *                   performed.
     * @param qName      The qualified name (with prefix), or the
     *                   empty string if qualified names are not available.
     * @param attributes The attributes attached to the element.  If
     *                   there are no attributes, it shall be an empty
     *                   Attributes object.
     * @throws SAXException -
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        this.qName = qName;

        if ("row".equals(qName)) {
            // start new row
            this.row = new ArrayList<>();
        } else if ("c".equals(qName)) {
            // if this element qname is 'c', the next element qname will be 'v'.

            // 获取单元格的位置，如 A1,B1
            String row = attributes.getValue("r");
            // style id
            String style = attributes.getValue("s");

            // data type：null or s or others
            this.cellType = attributes.getValue("t");
            if ("str".equals(cellType)) {
                print(attributes);
            }
        }
        // all q-names:
        // sheetData, sheetFormatPr, sheetPr, c, pageMargins, pageSetup, selection,
        // sheetView, v, sheetViews, worksheet, row, dimension, headerFooter, str
        super.startElement(uri, localName, qName, attributes);
    }

    /**
     * 接收元素结束的通知。
     * <p>
     * 每个元素的末尾执行特定操作（例如完成树节点或将输出写入文件）。
     *
     * @param uri       The Namespace URI, or the empty string if the
     *                  element has no Namespace URI or if Namespace
     *                  processing is not being performed.
     *                  e.g.: http://schemas.openxmlformats.org/spreadsheetml/2006/main
     * @param localName The local name (without prefix), or the
     *                  empty string if Namespace processing is not being
     *                  performed.
     * @param qName     The qualified name (with prefix), or the
     *                  empty string if qualified names are not available.
     * @throws SAXException -
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if ("row".equals(qName)) {
            this.consumer.accept(this.row);
        }
    }

    /**
     * 接收元素中字符数据的通知。
     * <p>
     * 对每个字符数据块采取特定的操作（例如将数据添加到节点或缓冲区，或将其打印到文件中）。
     *
     * @param ch     The characters.
     * @param start  The start position in the character array.
     * @param length The number of characters to use from the
     *               character array.
     * @throws SAXException -
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if ("v".equals(qName)) {
            String value = new String(ch, start, length);
            if (this.cellType == null) {
                this.row.add(value);
            } else if ("s".equals(this.cellType)) {
                int idx = Integer.parseInt(value);
                RichTextString text = sharedStrings.getItemAt(idx);
                this.row.add(text.getString());
//                this.row.add(value);
            } else if ("str".equals(this.cellType)) {
                this.row.add(value);
            } else {
                throw new RuntimeException("unknown cell type: " + this.cellType);
            }
        }
        super.characters(ch, start, length);
    }
}